/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 *
 * $Id: launcher.hpp,v 1.16 2009-05-13 07:18:19 amaula Exp $
 * 
 * \file
 * \brief Header file for Launcher class.
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 */
#ifndef _MACI_LAUNCHER_LAUNCHER_HPP_
#define _MACI_LAUNCHER_LAUNCHER_HPP_
#include "launchertypes.hpp"
#include "directory.hpp"
#include "executor.hpp"
#include <vector>


/** Multiple process execution controller class.
 */
class CLauncher
{
public:
  CLauncher();
  ~CLauncher();
  
  /** Execute a Machine.
   *
   * This functions attempts to read and execute the provided Machine
   * configuration file or directory. If the parameter points to
   * directory, this directory is searched for '*.mconf', files and
   * first found is executed.
   *
   * @param[in] aMachinePath    Path to machine configuration file 
   *                            or directory
   * @return                    'true' when the machine was succesfully
   *                            parsed and executed. 'false' when machine
   *                            execution failed for some reason. (See
   *                            the diagnostic prints)
   */
  bool ExecuteMachine(const std::string &aMachinePath);

  
  /** Build a Machine.
   *
   * This machine attempts to read configuration for a specified
   * machine, and to Build all modules required to run it. The build
   * depends on 'build' commands specified in the module configuration
   * files. 'build' commands are always run as 'non-root' user if
   * possible. Build is executed as single-threaded, sequential
   * operation.
   *
   * @param[in] aMachinePath    Path to machine configuration file 
   *                            or directory
   * @return                    'true' when the machine was succesfully
   *                            parsed and Built. 'false' when machine
   *                            build failed for some reason. (See
   *                            the diagnostic prints)
   */
  bool BuildMachine(const std::string &aMachinePath);


  /** Terminate the Machine.
   *
   * This function terminates all the modules belonging to currently
   * active machine.
   * 
   */
  bool TerminateMachine();


  /** Waits machine termination.
   *
   * This functions sits and waits until the running machine has
   * terminated.
   *
   */
  bool WaitMachine(bool aBlock = true);


  /** This reads the output pipes from all processes, and prints the outputs
   * (if ownDebug level allows) in format:
   *
   * \verbatim
   * ==<module name / process id>==: <output line>
   * \endverbatim
   * 
   * \note The 'timeout' is the timeout for whole operation. This
   * function doesn't properly use 'select' style approach, but rather
   * wait for all processes in order for output. This approach means
   * that if you have two processes of which only last one prints
   * anything, it will print with 'aTimeout_ms' intervals because the
   * functions waits the full timeout on the first process. This may
   * be fixed in the future if required.
   *
   * @param[in] aLevel          ownDebug level to use for printing.
   * @param[in] aTimeout_ms     Timeout to wait for new input before
   *                            returning. 
   */
  void ReadAndPrintMachineOutput(const int aLevel, 
                                 const unsigned int aTimeout_ms);


  /** Assign the default GIMnetAP host for machines.
   *
   * This assigns the default host to be used by the machine. If
   * machine already defines the value, this will be ignored.
   *
   * @param[in] aDefaultHost    Host name 
   *
   */
  void SetDefaultGIMnetAPHost(const std::string &aDefaultHost);



  /** Assign the default GIMnetAP port for machines.
   *
   * This assigns the default GIMnetAP port to be used by the
   * machine. If the machine already defines the value, this will be
   * ignored.
   *
   * @param[in] aDefaultPort    Host name 
   *
   */
  void SetDefaultGIMnetAPPort(const unsigned int &aDefaultPort);

private:
  CExecutor *ExecuteModule(const SMachineConfEntry &aMachineConfig,
                           const SModuleConfEntry &aModuleConfig);
    
  bool ReadMachine(const std::string &aMachinePath, 
                   SMachineConfEntry &aMachineConf, 
                   TModuleConfEntryArray &aModuleConfArray);
  bool ReadAllModules(TModuleConfEntryArray &aArray, const SMachineConfEntry &aMachine);
  static std::string FindMachineConfig(const std::string &aMachinePath);
  void GetNonRootCredentials(int &new_gid, int &new_uid);


  TStringArray GetConfsByPattern(const std::string &aDir, 
                                 const std::string &aPattern);
  std::string GetModuleGIMnetHost(const SMachineConfEntry &aMachineConfig,
                                  const SModuleConfEntry &aModuleConfig);
  std::string GetModuleGIMnetPort(const SMachineConfEntry &aMachineConfig,
                          const SModuleConfEntry &aModuleConfig);
  std::string GetModuleName(const SMachineConfEntry &aMachineConfig,
                            const SModuleConfEntry &aModuleConfig);
  std::string GetMaCIGroupName(const SMachineConfEntry &aMachineConfig,
                               const SModuleConfEntry &aModuleConfig);
    
  volatile bool iMachineActive;
  volatile bool iMachineTerminating;
  std::vector<CExecutor *> iActiveModules; ///< Active module instances
  SMachineConfEntry iMachineConfig; ///< Current  machine configuration
  TModuleConfEntryArray iModuleConfigArray; ///< Array of mod. confs.

  std::string iGIMnet_host; ///< GIMnet host, or zerolen if not set.
  unsigned int iGIMnet_port; ///< GIMnet port, or zero if not set.

};

#endif
